<!DOCTYPE HTML>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.5">
  <script src="../resources/testharness.js"></script>
  <script src="../resources/testharnessreport.js"></script>
  <style>
    #tab {
      transform: rotate(20deg);
    }
    #checkbox {
      transform: scaleY(1.5);
    }
    #group {
      transform: skewY(30deg);
    }
  </style>
</head>
<body>

<div id="container">
  <div role="group" id="group" tabindex="0">group</div>
  <div role="button" id="button" tabindex="0">button</div>
  <div role="tab" id="tab" tabindex="0">tab button</div>
  <div role="radio" id="radio" tabindex="0">radio</div>
  <div role="checkbox" id="checkbox" tabindex="0">checkbox</div>
  <div role="menuitem" id="menuitem" tabindex="0">menu item</div>
  <div role="menuitemcheckbox" id="menuitemcheckbox" tabindex="0">menu item checkbox</div>
  <div role="menuitemradio" id="menuitemradio" tabindex="0">menu item radio</div>
  <input id="radiobutton" type="radio" name="radiobutton" value="Radio Button">
  <label for="Radio Button">Radio Button</label>
  <button id="htmlbutton" >Click Me</button>
  <input id="inputbutton" type="button" value="Click Me">
</div>



<script>

const container = document.getElementById("container");
const events = ["pointerdown", "pointerup", "mousedown", "mouseup", "click"];
const htmlElementIds = ["group", "button", "tab", "radio", "checkbox", "menuitem",
    "menuitemcheckbox", "menuitemradio", "radiobutton", "htmlbutton", "inputbutton"];
const chromeMousePointerId = 1;
const mousePointerType = "mouse";
let eventLog = [];
let currentTest = null;
let preventDefaultOnPointerDown = false;

// Computes the center of the line segment defined by knownPoint and (unknownX, knownY)
// given the length of the segment.
function center_of_line_segment_knownY(knownPoint, knownY, length){
  let unknownX = knownPoint.x + Math.sqrt(Math.pow(length, 2) - Math.pow(knownY-knownPoint.y, 2));
  return {x:(knownPoint.x + unknownX)/2,
          y:(knownPoint.y + knownY)/2};
}

test(()=>{
  assert_object_equals(center_of_line_segment_knownY({x:0, y:0}, 10, 10), {x:0, y:5}, "Vertical line starting at 0,0");
  assert_object_equals(center_of_line_segment_knownY({x:0, y:5}, 15, 10), {x:0, y:10}, "Vertical line starting at 0,5");
  assert_object_equals(center_of_line_segment_knownY({x:0, y:0}, 8, 10), {x:3, y:4}, "Diagonal line starting at 0,0");
  assert_object_equals(center_of_line_segment_knownY({x:0, y:1}, 9, 10), {x:3, y:5}, "Diagonal line starting at 0,1");
}, "Center of line when starting point and end point's y coordinate is known");

// Computes the center of the line segment defined by knownPoint and (knownX, unknownY)
// given the length of the segment.
function center_of_line_segment_knownX(knownPoint, knownX, length){
  let transposed_center = center_of_line_segment_knownY({x:knownPoint.y, y:knownPoint.x}, knownX, length);
  return {x:transposed_center.y, y:transposed_center.x};
}

test(()=>{
  assert_object_equals(center_of_line_segment_knownX({x:0, y:0}, 10, 10), {x:5, y:0}, "Horizontal line starting at 0,0");
  assert_object_equals(center_of_line_segment_knownX({x:5, y:0}, 15, 10), {x:10, y:0}, "Horizontal line starting at 0,5");
  assert_object_equals(center_of_line_segment_knownX({x:0, y:0}, 8, 10), {x:4, y:3}, "Diagonal line starting at 0,0");
  assert_object_equals(center_of_line_segment_knownX({x:1, y:0}, 9, 10), {x:5, y:3}, "Diagonal line starting at 0,1");
}, "Center of line when starting point and end point's x coordinate is known");

function compute_rect_center(domRect){
  if((domRect.x === domRect.left || domRect.x === domRect.right) && (domRect.y === domRect.top || domRect.y === domRect.bottom))
    return {x: (domRect.left + domRect.right) / 2,
            y: (domRect.top + domRect.bottom) / 2};
  let diagonal_length = Math.sqrt(Math.pow(domRect.height, 2) + Math.pow(domRect.width, 2));
  let origin_point = {x:domRect.x, y:domRect.y};
  if(domRect.x !== domRect.left && domRect.x !== domRect.right){
    // We must have domRect.y be either top or bottom.
    if(domRect.y === domRect.top)
      return center_of_line_segment_knownY(origin_point, domRect.bottom, diagonal_length);
    return center_of_line_segment_knownY(origin_point, domRect.top, diagonal_length);
  }
  if(domRect.x === domRect.left)
    return center_of_line_segment_knownX(origin_point, domRect.right, diagonal_length);
  return center_of_line_segment_knownX(origin_point, domRect.left, diagonal_length);
}

test(()=>{
  assert_object_equals(compute_rect_center({x:2,y:2,top:2,left:2,bottom:12, right:12, width:10, height:10}), {x:7, y:7}, "Not rotated rectangle");
  assert_object_equals(compute_rect_center({x:1,y:2,top:2,left:-1,bottom:-2, right:3, width:Math.sqrt(8), height:Math.sqrt(8)}), {x:1, y:0}, "Rotated rectangle, origin at (1,2)");
  assert_object_equals(compute_rect_center({x:-1,y:0,top:2,left:-1,bottom:-2, right:3, width:Math.sqrt(8), height:Math.sqrt(8)}), {x:1, y:0}, "Rotated rectangle, origin at (-1,0)");
  assert_object_equals(compute_rect_center({x:3,y:0,top:2,left:-1,bottom:-2, right:3, width:Math.sqrt(8), height:Math.sqrt(8)}), {x:1, y:0}, "Rotated rectangle, origin at (3,0)");
}, "Center of rectangle given a DomRect");

events.forEach((ev)=>container.addEventListener(ev, (e)=>{
  let message = e.target.id + "_" + ev;
  eventLog.push(message);

  if(e.type === "pointerdown" || e.type === "pointerup" || e.type === "click"){
    currentTest.step(()=>{
      assert_equals(e.pointerId, chromeMousePointerId, `Event's ${ev} pointer Id matches the pointer id of the mouse`);
      assert_equals(e.pointerType, mousePointerType, `Event's ${ev} pointer type is "mouse"`);
      assert_true(e.isPrimary, `Event's ${ev} pointer is the primary pointer`);
    });
  }
  currentTest.step(()=>{
    let elementRect = document.getElementById(e.target.id).getBoundingClientRect();
    let center = compute_rect_center(elementRect);
    assert_approx_equals(e.x, center.x, 1, `Event's ${ev} x position is the same with x position of the center of element with ${e.target.id} id`);
    assert_approx_equals(e.y, center.y, 1, `Event's ${ev} y position is the same with y position of the center of element with ${e.target.id} id`);
    assert_approx_equals(e.screenX, window.screenX + center.x, 1, `Event's ${ev} screenX position is the same correct for element with ${e.target.id} id`);
    assert_approx_equals(e.screenY, window.screenY + center.y, 1, `Event's ${ev} screenX position is the same correct for element with ${e.target.id} id`);
    assert_equals(e.movementX, 0, `Event's ${ev} movementX must be 0.`);
    assert_equals(e.movementY, 0, `Event's ${ev} movementY must be 0.`);
    assert_equals(e.button, 0, `Event's ${ev} primary button pressed.`);
    if(ev === "pointerdown" || ev === "mousedown" || ev === "click")
      assert_equals(e.buttons, 1, `Event's ${ev} primary button pressed.`);
    else
      assert_equals(e.buttons, 0, `For event ${ev} there are no buttons pressed.`);
    if(ev === "click")
      assert_equals(e.detail, 1, "Click event has a click count of 1");
    else
      assert_equals(e.detail, 0, `For event ${ev} click count should not be populated.`);
  });

  if(preventDefaultOnPointerDown && ev === "pointerdown")
    e.preventDefault();
}));

function test_without_pointerdown_prevented(test, zoom_factor){
  assert_true(!!window.testRunner, "window.testRunner unavailable");
  testRunner.setPageZoomFactor(zoom_factor);
  currentTest = test;
  eventLog = [];
  let eventList = ["pointerdown", "mousedown",
                  "pointerup", "mouseup", "click"];

  assert_true(focusElements(true), "window.accessibilityController unavailable");
  // All events coordinates are tested in the generic event handlers.
  assert_array_equals(eventLog, generateExpectedLog(eventList, htmlElementIds));
}

promise_test((test) => new Promise((resolve, reject)=>{
  test_without_pointerdown_prevented(test, 1) ;
  resolve();
}), 'Tests that pointer events, mouse events and click are fired for accessibility press');

promise_test((test) => new Promise((resolve, reject)=>{
  assert_true(!!window.testRunner, "window.testRunner unavailable");
  testRunner.setPageZoomFactor(5);
  currentTest = test;
  eventLog = [];
  let eventList = ["pointerdown", "mousedown",
                  "pointerup", "mouseup", "click"];
  assert_true(!!window.accessibilityController, "window.accessibilityController unavailable");
  let element = document.getElementById("tab");
  tab.focus();
  accessibilityController.focusedElement.press();
  // All events coordinates are tested in the generic event handlers.
  assert_array_equals(eventLog, generateExpectedLog(eventList, ["tab"]));
  resolve();
}), "Tests that pointer events, mouse events and click's coordinates are populated correctly for accessibility press when zoomed in");


function test_with_pointerdown_prevented(test, zoom_factor, makeButtonDirty){
  assert_true(!!window.testRunner, "window.testRunner unavailable");
  testRunner.setPageZoomFactor(zoom_factor);
  currentTest = test;
  eventLog = [];
  let eventList = ["pointerdown", "pointerup", "click"];
  preventDefaultOnPointerDown = true;

  assert_true(focusElements(makeButtonDirty), "window.accessibilityController unavailable");
  // All events coordinates are tested in the generic event handlers.
  assert_array_equals(eventLog, generateExpectedLog(eventList, htmlElementIds));
}

function generateExpectedLog(eventList, items){
  let expectedLog = [];
  items.forEach((item)=>
    eventList.forEach((ev)=>
      expectedLog.push(item + "_" + ev)));
  return expectedLog;
}

// Returns true if accessibilityController exists.
function focusElements(makeButtonDirty){
  if (window.accessibilityController) {
    for (var k = 0; k < htmlElementIds.length; k++) {
       let element = document.getElementById(htmlElementIds[k]);
       // For button, change position so we test that the coordinates are calculated correctly.
       if(element.id === "button" && makeButtonDirty)
         element.style.left = '100px';
       element.focus();
       accessibilityController.focusedElement.press();
    }
  }
  return !!window.accessibilityController;
}
</script>
</body>
